package zyutils

import (
	"log"
	"strings"
)

type ReadCsvFile struct {
	ReadFile
	Headers   []string
	delimiter string
}

func (f *ReadCsvFile) Open(file Path, delimiter string) {
	f.ReadFile.Open(file)
	f.delimiter = delimiter
}

func (f *ReadCsvFile) ReadHeader(comment string) {
	for {
		line, isEOF := f.ReadLine()
		if !isEOF {
			if len(line) == 0 || (comment != "" && strings.HasPrefix(line, comment)) {
				continue
			}
			f.Headers = strings.Split(line, f.delimiter)
		}
		break
	}
}

func (f ReadCsvFile) CheckHeader(keys []string) {
	for _, key := range keys {
		pass := false
		for _, header := range f.Headers {
			if strings.Contains(header, key) {
				pass = true
				break
			}
		}
		if !pass {
			log.Panicf("Error: can not find %v in headers", key)
		}
	}
}

func (f ReadCsvFile) ReadMap() (map[string]string, bool) {
	line, isEOF := f.ReadLine()
	if isEOF {
		return map[string]string{}, true
	}
	fileds := strings.Split(line, f.delimiter)
	if len(fileds) != len(f.Headers) {
		log.Panicf("the colnum is not equal with header: %s", line)
	}
	data := make(map[string]string)
	for i := 0; i < len(fileds); i++ {
		data[f.Headers[i]] = fileds[i]
	}
	return data, false
}

func (f ReadCsvFile) ReadMaps() []map[string]string {
	allData := make([]map[string]string, 0)
	for {
		data, isEOF := f.ReadMap()
		if isEOF {
			break
		}
		allData = append(allData, data)
	}
	return allData
}

func NewReadCsvFile[T string | Path](file T, delimiter string, comment string) ReadCsvFile {
	f := ReadCsvFile{}
	f.Open(Path(file), delimiter)
	f.ReadHeader(comment)
	return f
}

type WriteCsvFile struct {
	WriteFile
	Headers   []string
	delimiter string
}

func (f *WriteCsvFile) Open(file Path, delimiter string) {
	f.WriteFile.Open(file)
	f.delimiter = delimiter
}

func (f *WriteCsvFile) WritedHeader(header []string) {
	f.Headers = header
	f.WriteLine(strings.Join(f.Headers, f.delimiter) + "\n")
}

func (f WriteCsvFile) WriteMap(data map[string]string) {
	fileds := make([]string, len(f.Headers))
	for i, col := range f.Headers {
		if val, ok := data[col]; ok {
			fileds[i] = val
		} else {
			fileds[i] = "."
		}
	}
	f.WriteLine(strings.Join(fileds, f.delimiter) + "\n")
}

func NewWriteCsvFile[T string | Path](file T, delimiter string, header []string) WriteCsvFile {
	f := WriteCsvFile{}
	f.Open(Path(file), delimiter)
	f.WritedHeader(header)
	return f
}
